/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: cbus_message.h
 *
 * Purpose: the message define for cbus
 *
 * Developer:
 *   wen.gu , 2021-06-15
 *
 * TODO:
 *
 ***************************************************************************/

/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __CBUS_MESSAGE_H__
#define __CBUS_MESSAGE_H__

#include "cbus/core/cbus_type.h"
#include "cbus/core/cbus_buffer.h"

/******************************************************************************
 **    MACROS
 ******************************************************************************/

/** the protocol version of message only 1 byte */
#define CBUS_MESSAGE_PROTOCOL_VERSION ((GU08)01)


#define CBUS_MESSAGE_START_CODE ((GU08)0xe5)
#define CBUS_MESSAGE_END_CODE ((GU08)0xe6)

#define CBUS_MSG_HDR_SIZE (12)
/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
G_C_HEADER_START

typedef enum _cbus_message_type
{
    CBUS_MSG_TYPE_Unknown = 0,
    CBUS_MSG_TYPE_Method_Request           = 0x01, /** (client --> service) the payload is the parameter of request (need response @see CBUS_MSG_TYPE_Method_Response) */
    CBUS_MSG_TYPE_Method_Request_No_Return = 0x02, /** (client --> service) the payload is the parameter of request (needn't response)  */
    CBUS_MSG_TYPE_Method_Response          = 0x03, /** (service --> client) the payload is the response data of request (needn't response)  */

    CBUS_MSG_TYPE_Event                    = 0x04, /** (service --> client) the payload is the event content (needn't response)  */
    CBUS_MSG_TYPE_Event_Subscribe          = 0x05, /** (client --> service) no payload, just message header only (need response @see CBUS_MSG_TYPE_Event_Subscribe_Ack) */
    CBUS_MSG_TYPE_Event_Subscribe_Group    = 0x06, /** (client --> service) the payload is a array of event id(message id must be aways 0). (need response @see CBUS_MSG_TYPE_Event_Subscribe_Ack) */
    CBUS_MSG_TYPE_Event_Subscribe_Ack      = 0x07, /** (service --> client) no payload, have return code, @see cbus_message_return_code_t (needn't response) */
    CBUS_MSG_TYPE_Event_Stop_Subscribe     = 0x08, /** (client --> service) no payload, just message header only (need response @see CBUS_MSG_TYPE_Event_Stop_Subscribe_Ack)*/
    CBUS_MSG_TYPE_Event_Stop_Subscribe_Ack = 0x09, /** (service --> client) no payload, have return code, @see cbus_message_return_code_t (needn't response) */

    CBUS_MSG_TYPE_Property_Set             = 0x0a, /** (client --> service) the payload is the parameter of request (need response @see CBUS_MSG_TYPE_Property_Set_Ack) */
    CBUS_MSG_TYPE_Property_Set_Ack         = 0x0b, /** (service --> client) the payload is the response data of request, have return code, @see cbus_message_return_code_t (needn't response)  */
    CBUS_MSG_TYPE_Property_Get             = 0x0c, /** (client --> service) the payload is the parameter of request (need response @see CBUS_MSG_TYPE_Property_Get_Ack) */
    CBUS_MSG_TYPE_Property_Get_Ack         = 0x0d, /** (service --> client) the payload is the response data of request, have return code, @see cbus_message_return_code_t  (needn't response)  */
    CBUS_MSG_TYPE_Property_Notification    = 0x0e, /** (service --> client) the payload is the value content of property (needn't response)  */

    CBUS_MSG_TYPE_Property_Subscribe           = 0x0f, /** (client --> service) no payload, just message header only (need response @see CBUS_MSG_TYPE_Property_Subscribe_Ack) */
    CBUS_MSG_TYPE_Property_Subscribe_Ack       = 0x10, /** (service --> client) no payload, have return code, @see cbus_message_return_code_t (needn't response) */
    CBUS_MSG_TYPE_Property_Stop_Subscribe      = 0x11, /** (client --> service) no payload, just message header only (need response @see CBUS_MSG_TYPE_Property_Stop_Subscribe_Ack) */
    CBUS_MSG_TYPE_Property_Stop_Subscribe_Ack  = 0x12, /** (service --> client) no payload, have return code, @see cbus_message_return_code_t (needn't response) */

    CBUS_MSG_TYPE_MAX, /** a helper value for program, add new type must above this field */
}cbus_message_type_t;

typedef enum _cbus_message_return_code
{
    CBUS_MSG_OK                         = 0, /** No error occurred */
    CBUS_MSG_ErrUndefined               = 0x01, /** An unspecified error occurred */
    CBUS_MSG_ErrUnknownService          = 0x02, /** */
    CBUS_MSG_ErrUnknownMethod           = 0x03, /** unknown method(maybe not implementation) */
    CBUS_MSG_ErrNotReady                = 0x04, /** Application not running.*/
    CBUS_MSG_ErrNotReachable            = 0x05, /** System running the service is not reachable (internal error code only). */
    CBUS_MSG_ErrTimeout                 = 0x06, /** A timeout occurred (internal error code only). */
    CBUS_MSG_ErrWrongProtocolVersion    = 0x07, /** Version of cbus protocol not supported*/ 
    CBUS_MSG_ErrWrongInterfaceVersion   = 0x08, /** Interface version mismatch */
    CBUS_MSG_ErrMalformedMessage        = 0x09, /** Deserialization error, so that payload cannot be deserialized. */
    CBUS_MSG_ErrWrongMessageType        = 0x0a, /** An unexpected message type was received */

    /** reserved for cbus protocol, <= 0x1f */

    
    CBUS_MSG_ErrInsufficientResources   = 0x20, /** not enough resource */
    CBUS_MSG_ErrNotFound                = 0x21, /** not found */

    CBUS_MSG_ErrStartUnused = 0x3f,  /** Reserved for specific errors of services and methods. These errors are specified by the interface specification. */
    
    CBUS_MSG_ErrMax = 0xff,
}cbus_message_return_code_t;


typedef GPTR cbus_endpoint_id;


typedef GU16 cbus_message_id;
typedef GU16 cbus_session_id;

/**
 * @brief define the communication message for cbus, the member of message:
 * 
 * - length(4byte):            the total length of whole message
 * - message_id(2byte):        the id of message
 * - session_id(2byte):        the session id of communication
 * - protocol_version(1byte):  the version of current communication protocol
 * - interface_version(1byte): the version of interface which service provided
 * - message_type(1byte):      the type of message
 * - return_code(1byte):       the response message return's status code
 * - payload:                  the payload data of current message
 * ////////////////////////////////////////////////////////////////////////
 * the communication protocol
 * |length(4byte)|message id(2byte)|session id(2byte)|protocol_version(1byte)|interface_version(1byte)|message type(1byte)|return code(1byte)|payload(variable)|
 */

#if 0
typedef struct _cbus_message
{
    //GU32 length;  /** the length only used transport, can be calculate with: CBUS_MSG_HDR_SIZE + payload_size */
    cbus_message_id message_id;
    cbus_session_id session_id;
    GU08 protocol_version;
    GU08 interface_version;
    GU08 message_type;
    GU08 return_code; /** the code, could be a error code return as response */
    
    cbus_buffer_t payload;

    /** used for take more infroamtion */
    cbus_endpoint_id endpoint_id; /** enpoint id or client id */
    void* endpoint_ref; /** a reference of endpoint instance, indicate current message come from which endpoint */
}cbus_message_t;
#else

struct _cbus_endpoint;
typedef struct _cbus_endpoint* cbus_endpoint_t;


typedef GPTR cbus_message_t;
#endif



/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/
G_API cbus_message_t cbus_message_alloc(GU32 payload_size);
G_API cbus_message_t cbus_message_alloc_response(cbus_message_t request_message, GU32 response_payload_size);
G_API cbus_message_t cbus_message_reference(cbus_message_t message);
G_API void cbus_message_free(cbus_message_t message);

G_API void cbus_message_set_message_id(cbus_message_t message, cbus_message_id msg_id);
G_API void cbus_message_set_session_id(cbus_message_t message, cbus_session_id session_id);
G_API void cbus_message_set_message_type(cbus_message_t message, cbus_message_type_t msg_type);
G_API void cbus_message_set_protocol_version(cbus_message_t message, GU08 protocol_version);
G_API void cbus_message_set_interface_version(cbus_message_t message, GU08 interface_version);
G_API void cbus_message_set_return_code(cbus_message_t message, GU08 return_code);
G_API void cbus_message_set_endpoint_id(cbus_message_t message, cbus_endpoint_id endpoint_id);
G_API void cbus_message_set_endpoint_ref(cbus_message_t mesage, cbus_endpoint_t endpoint_ref);
G_API void cbus_message_set_opaque(cbus_message_t message, GPTR opaque);
G_API void cbus_message_set_start_code(cbus_message_t message, GU08 start_code);
G_API void cbus_message_set_end_code(cbus_message_t message, GU08 end_code);

G_API cbus_message_id cbus_message_get_message_id(cbus_message_t message);
G_API cbus_session_id cbus_message_get_session_id(cbus_message_t message);
G_API cbus_message_type_t cbus_message_get_message_type(cbus_message_t message);
G_API GU08 cbus_message_get_protocol_version(cbus_message_t message);
G_API GU08 cbus_message_get_interface_version(cbus_message_t message);
G_API GU08 cbus_message_get_return_code(cbus_message_t message);
G_API cbus_endpoint_id cbus_message_get_endpoint_id(cbus_message_t message);
G_API cbus_endpoint_t cbus_message_get_endpoint_ref(cbus_message_t message);
G_API GPTR cbus_message_get_opaque(cbus_message_t message);
G_API GBUF cbus_message_get_data(cbus_message_t message);
G_API GU32 cbus_message_get_size(cbus_message_t message);
G_API GBUF cbus_message_get_payload(cbus_message_t message);
G_API GU32 cbus_message_get_payload_size(cbus_message_t message);
G_API GU08 cbus_message_get_start_code(cbus_message_t message);
G_API GU08 cbus_message_get_end_code(cbus_message_t message);

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////

/**
 * @brief this is a helper API, if set endpoint_ref and endpoint_id then can be call this API to send message to remote side
 * 
 * @param message [in] the message which need to send to remote side
 * @return GErrc :success: G_OK, else: error code @see GErrc
 * @note before call this API, the 'message' must be set endpoint_ref and endpoint_id
 */
G_API GErrc cbus_message_send(cbus_message_t message);


/**
 * @brief a helper API, create a response message with request message and send response message to remote side.
 * 
 * @param request_message [in] a request message which will be provide base info for response message(e.g. msg id,type, session id, endpoint ref endpoint id and so on)
 * @param return_code     [in] the response message haven't payload, just a header, this is the return code which need send to remote side with response message
 * @return GErrc :success: G_OK, else: error code @see GErrc
 * @note  before call this API, the 'request_message' must have valid member: endpoint_ref and endpoint_id
 */
G_API GErrc cbus_message_send_response_code(cbus_message_t request_message, GU08 return_code);


//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
G_API cbus_message_return_code_t cbus_gerrc_to_msg_return_code(GErrc errc);
G_API GErrc cbus_msg_return_code_to_gerrc(cbus_message_return_code_t return_code);


/** get the response message type with request type */
G_API GU08 cbus_message_get_response_type(GU08 request_message_type);

/** get the request message type with response type */
G_API GU08 cbus_message_get_request_type(GU08 response_message_type);

///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
G_API const GSTR cbus_message_type_to_str(cbus_message_type_t msg_type);


G_C_HEADER_END



#endif /** !__CBUS_MESSAGE_H__ */

